package she

import java.net.InetAddress

import java.nio.ByteBuffer
import java.nio.channels.SocketChannel

/** A connexion to an 'HttpServer'. It stores all information collected about
  * the client throughout its interaction with the server. */
case class HttpConnexion(input: SocketChannel) { thisConnexion =>
  
  /** An exception thrown when a method that assumess the connexion still to
    * be open is called on a connexion that has been dropped. */
  case class DroppedConnexionException extends Exception
  
  /** Returns 'body' only if this connexion is still open, and throws a
    * 'DroppedConnexionException' otherwise */
  private def confirmIsOpen[T](body: => T): T =
    if (input.isOpen)
      body
    else
      throw DroppedConnexionException()
  
  /** The single byte that was read from the connexion to test if the client
    * is sending data on it. This byte must be prepended to any additional
    * bytes read from the connexion before parsing them. */
  private val unparsed: ByteBuffer =
    ByteBuffer.allocate(1)
  
  /** The partially built request that is constructed when 'parseBytes' is
    * called with not enough bytes available on the connexion, if any. */
  private var partialRequest: Option[PartialRequest] = None
  
  /** Checks whether any bytes are available to read from this connexion,
    * i.e. whether the client has started sending a new request or is ready to
    * complete a previous, partial request on this connexion. */
  def bytesAvailable: Boolean = confirmIsOpen {
    false // TODO: actually check something
  }
  
  /** Tries to parse the bytes that can be read from this connexion as an
    * HTTP request. There are three possible situations:
    * - A valid and complete HTTP request is parsed from the bytes that
    *   can be read without blocking. In this case, a 'FullRequest' of the
    *   proper type is returned.
    * - The bytes that can be read from the connexion do not form a
    *   prefix of any valid HTTP request. In this case, an 'InvalidRequest'
    *   is returned.
    * - The connexion runs-out of available bytes to read without blocking
    *   before any of the previous two situations can be determined. In this
    *   case, a 'PartialRequest' is returned. */
  def parseBytes: HttpRequest = confirmIsOpen {
    new InvalidRequest { // TODO: actually parse something
      val connexion = thisConnexion
    }
  }	
  
  /** The address of the client that initiated this connexion. */
  def clientAddress: InetAddress =  confirmIsOpen {
    input.socket.getInetAddress
  }
  
  /** Closes this connexion without sending any further data to the client.
    * No further action can be taken on the dropped connexion. A connexion
    * should only be dropped if the server is overloaded and cannot return
    * a proper HTTP error message, or if the client is behaving in a
    * threatening manner.  */
  def drop =
    if (input.isOpen) input.close
  
}
